home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / Peter Lewis / Finger 1.3.5 / source / TCP Units / TCPStuff.unit < prev    next >
Encoding:
Text File  |  1992-02-24  |  20.9 KB  |  718 lines  |  [TEXT/PJMM]

  1. unit TCPStuff;
  2.  
  3. { This code is part of the Finger/Fingerd source code, written in THINK Pascal 4 }
  4. { Copyright 1991-1992 Peter N Lewis }
  5. { If you use this code, you must give me credit in your about box and documentation }
  6. { This is part of my TCP library of routines }
  7.  
  8.  
  9. interface
  10.  
  11.     uses
  12.         TCPTypes;
  13.  
  14.     const
  15.         TCPBUFFERSIZE = 4096;    { Amount of space to allocate for each TCP connection }
  16.         INCOMINGBUFSIZE = 100;    { Incoming buffer size, used for buffering ReceiveUpTo. }
  17.         control_block_max = 260;
  18.         tooManyControlBlocks = -23098;
  19.  
  20.     type
  21.         OSErrPtr = ^OSErr;
  22.  
  23. { TCP connection description: }
  24.         TCPConnectionType = record
  25.                 magic: OSType;    { A magic number to try and avoid problems with released connection IDs. }
  26.                 stream: StreamPtr;
  27.                 asends, asendcompletes: longInt;
  28.                 closedone: boolean;
  29.                 closeuserptr: OSErrPtr;
  30.                 incomingPtr: Ptr;                                { Pointer into inBuf of next byte to read. }
  31.                 incomingSize: longInt;                        { Number of bytes left in inBuf. }
  32.                 buffer: array[1..TCPBUFFERSIZE] of SignedByte;        { connection buffer. }
  33.                 inBuf: array[1..INCOMINGBUFSIZE] of SignedByte;    {Input buffer. }
  34.             end;
  35.         TCPConnectionPtr = ^TCPConnectionType;
  36.  
  37.         MyControlBlock = record
  38.                 tcp: TCPControlBlock;
  39.                 inuse: boolean;
  40.                 userptr: OSErrPtr;
  41.                 proc: procPtr;
  42.                 tcpc: TCPConnectionPtr;
  43.             end;
  44.         MyControlBlockPtr = ^MyControlBlock;
  45.  
  46.  
  47.         TCPStateType = (T_WaitingForOpen, T_Closed, T_Listening, T_Opening, T_Established,{}
  48.             T_Closing, T_PleaseClose, T_Unknown);
  49.  
  50.     function TCPNameToAddr (var hostName: str255; timeout: longInt; var hostFile: str255): longInt;
  51.     function TCPOpenResolver (var hostFile: str255; var dataptr: ptr): OSErr;
  52.     function TCPStrToAddr (dataptr: ptr; var hostName: str255; var rtnStruct: hostInfo; var done: signedByte): OSErr;
  53.     procedure TCPAddrToStr (dataptr: ptr; addr: longInt; var addrStr: str255);
  54.     function TCPAddrToName (dataptr: ptr; addr: longInt; var rtnStruct: hostInfo; var done: signedByte): OSErr;
  55.     procedure TCPCloseResolver (dataptr: ptr);
  56.  
  57.     function C2PStr (s: stringPtr): stringPtr;
  58.     procedure SanitizeHostName (var s: str255);
  59.  
  60.     function TCPInit: OSErr;
  61.     procedure TCPFinish;
  62.     function TCPGetMyIPAddr (var myIP: longInt): OSErr;
  63.     function TCPActiveOpen (var connection: TCPConnectionPtr; localport: integer; remoteIP: longInt; remoteport: integer; userptr: OSErrPtr): OSErr;
  64.     function TCPPassiveOpen (var connection: TCPConnectionPtr; localport: integer; remoteIP: longInt; remoteport: integer; userptr: OSErrPtr): OSErr;
  65.     function TCPFlush (connection: TCPConnectionptr): OSErr;
  66.     function TCPClose (connection: TCPConnectionPtr; userptr: OSErrPtr): OSErr;
  67.     function TCPAbort (connection: TCPConnectionPtr): OSErr;
  68.     function TCPRelease (var connection: TCPConnectionPtr): OSErr;
  69.     procedure TCPRawState (connection: TCPConnectionPtr; var state: integer; var localhost: longInt; var localport: integer; var remotehost: longInt; var remoteport: integer; var available: longInt);
  70.     function TCPState (connection: TCPConnectionPtr): TCPStateType;
  71.     function TCPCharsAvailable (connection: TCPConnectionPtr): longInt;
  72.     function TCPRawReceiveChars (connection: TCPConnectionPtr; returnPtr: ptr; readCount: integer): OSErr;
  73. { Use EITHER RawReceive, or the other Receives.  Don't combine them for one stream! }
  74.     function TCPReceiveChars (connection: TCPConnectionPtr; returnPtr: ptr; readCount: integer): OSErr;
  75.     function TCPReadByte (connection: TCPConnectionPtr; timeout: longInt; var b: SignedByte): OSErr;
  76.     function TCPReceiveUpTo (connection: TCPConnectionPtr; termChar: signedByte;{}
  77.                                     charTimeOut: longInt; readPtr: ptr; readSize: longInt; var readPos: longInt;{}
  78.                                     var gottermchar: boolean): OSErr;
  79.     function TCPSend (connection: TCPConnectionPtr; writePtr: ptr; writeCount: integer): OSErr;
  80.     function TCPSendAsync (connection: TCPConnectionPtr; writePtr: ptr; writeCount: integer; userptr: OSErrPtr): OSErr;
  81.  
  82. implementation
  83.  
  84. {    Loosely based on code by Harry Chesley 12/88, thus Copyright © 1988 Apple Computer, Inc.}
  85. {    Converted to sensible pascal interface 7/91 by Peter Lewis, thus also Copyright © 1991 Peter Lewis }
  86.  
  87.     const
  88.         MAGICNUMBER = 'TMGK';    { Unique value used to trap illegal connection IDs. }
  89.         dispose_block_max = 100;
  90.  
  91.     type
  92.         MyControlBlockArray = array[1..control_block_max] of MyControlBlockPtr;
  93.  
  94.     var
  95.         driver_refnum: integer;
  96.         controlblocks: MyControlBlockArray;
  97.         max_dispose_block: integer;
  98.         disposeblocks: array[1..dispose_block_max] of ptr;
  99.  
  100.     procedure SanitizeHostName (var s: str255);
  101.         var
  102.             dummysp: stringPtr;
  103.     begin
  104.         dummysp := C2PStr(@s);
  105. {$PUSH}
  106. {$R-}
  107.         if s[Length(s)] = '.' then
  108.             s[0] := chr(Length(s) - 1);
  109. {$POP}
  110.     end;
  111.  
  112.     function GetA6: ptr;
  113.     inline
  114.         $2F4E, $0000;
  115.  
  116.     procedure CallCompletion (cbp: MyControlBlockPtr; addr: procPtr);
  117.     inline
  118.         $205F, $4E90;
  119.  
  120. {$PUSH}
  121. {$D-}
  122.     procedure IOCompletion; { All C functions look like pascal paramterless procedures from the procs point of view }
  123.         type
  124.             stackframe = packed record
  125.                     frameptr: ptr;
  126.                     returnptr: ptr;
  127.                     paramblockptr: MyControlBlockPtr;
  128.                 end;
  129.             stackframeptr = ^stackframe;
  130.         var
  131.             a6: stackframeptr;
  132.             cbp: MyControlBlockPtr;
  133.     begin
  134.         a6 := stackframeptr(GetA6);
  135.         cbp := a6^.paramblockptr;
  136.         with cbp^ do begin
  137.             if userptr <> nil then
  138.                 userptr^ := cbp^.tcp.ioResult;
  139.             inuse := false;
  140.             if proc <> nil then
  141.                 CallCompletion(cbp, proc);
  142.         end;
  143.     end;
  144. {$POP}
  145.  
  146.     procedure ZotBlocks;
  147.     begin
  148.         while max_dispose_block > 0 do begin
  149.             DisposPtr(disposeblocks[max_dispose_block]);
  150.             max_dispose_block := max_dispose_block - 1;
  151.         end;
  152.     end;
  153.  
  154.     procedure AddBlock (p: univ ptr);
  155.     begin
  156.         if max_dispose_block < dispose_block_max then begin
  157.             max_dispose_block := max_dispose_block + 1;
  158.             disposeblocks[max_dispose_block] := p;
  159.         end;
  160.     end;
  161.  
  162.     procedure ZeroCB (var cb: TCPControlBlock; stream: StreamPtr; call: integer);
  163.     { Zero out the control block parameters. }
  164.         var
  165.             i: integer;
  166.             p: longInt;
  167.     begin
  168.         ZotBlocks;
  169.         for p := longInt(@cb) to longInt(@cb) + SizeOf(TCPControlBlock) - 1 do
  170.             ptr(p)^ := 0;
  171.         cb.tcpStream := stream;
  172.         cb.ioCRefNum := driver_refnum;
  173.         cb.csCode := call;
  174.     end;
  175.  
  176.     function GetCB (var cbp: MyControlBlockPtr; tcpc: TCPConnectionPtr; call: integer; userptr: OSErrPtr; proc: procptr): OSErr;
  177. { NOTE: Must not move memory if there is a free block available (ie, during a Completion call) }
  178.         var
  179.             i: integer;
  180.     begin
  181.         i := 1;
  182.         while (i < control_block_max) & (controlblocks[i] <> nil) & controlblocks[i]^.inuse do
  183.             i := i + 1;
  184.         cbp := controlblocks[i];
  185.         if cbp = nil then begin
  186.             cbp := MyControlBlockPtr(NewPtr(SizeOf(MyControlBlock)));
  187.             if cbp <> nil then begin
  188.                 cbp^.inuse := false;
  189.                 controlblocks[i] := cbp;
  190.             end;
  191.         end;
  192.         if (cbp <> nil) & not cbp^.inuse then begin
  193.             ZeroCB(cbp^.tcp, tcpc^.stream, call);
  194.             cbp^.tcp.ioCompletion := @IOCompletion;
  195.             cbp^.inuse := true;
  196.             cbp^.userptr := userptr;
  197.             cbp^.tcpc := tcpc;
  198.             cbp^.proc := proc;
  199.             if userptr <> nil then
  200.                 userptr^ := inprogress;
  201.             GetCB := noErr;
  202.         end
  203.         else begin
  204.             cbp := nil;
  205.             GetCB := outOfMemory;
  206.         end;
  207.     end;
  208.  
  209.     procedure FreeCB (var cbp: MyControlBlockPtr);
  210.     begin
  211.         if cbp <> nil then
  212.             cbp^.inuse := false;
  213.         cbp := nil;
  214.     end;
  215.  
  216. {$S Init}
  217.     function TCPInit: OSErr;
  218.         var
  219.             oe: OSErr;
  220.             i: integer;
  221.     begin
  222.         max_dispose_block := 0;
  223.         oe := OpenDriver('.IPP', driver_refnum);
  224.         for i := 1 to control_block_max do
  225.             controlblocks[i] := nil;
  226.         TCPInit := oe;
  227.     end;
  228.  
  229. {$S Term}
  230.     procedure TCPFinish;
  231.         var
  232.             i: integer;
  233.     begin
  234.         for i := 1 to control_block_max do
  235.             if controlblocks[i] <> nil then begin
  236.                 DisposPtr(ptr(controlblocks[i]));
  237.                 controlblocks[i] := nil;
  238.             end;
  239.     end;
  240.  
  241. {$S}
  242.     procedure DestroyConnection (var connection: TCPConnectionPtr);
  243.     begin
  244.         connection^.magic := '????';
  245.         DisposPtr(Ptr(connection));
  246.         connection := nil;
  247.     end;
  248.  
  249.     function ValidateConnection (connection: TCPConnectionPtr): OSErr;
  250.     begin
  251.         if connection = nil then
  252.             ValidateConnection := connectionDoesntExist
  253.         else if connection^.magic <> MAGICNUMBER then
  254.             ValidateConnection := connectionDoesntExist
  255.         else
  256.             ValidateConnection := noErr;
  257.     end;
  258.  
  259.     function PBControlSync (var cb: TCPControlBlock): OSErr;
  260.     begin
  261.         PBControlSync := PBControl(@cb, false);
  262.     end;
  263.  
  264.     function PBControlAsync (var cbp: MyControlBlockPtr): OSErr;
  265.         var
  266.             oe: OSErr;
  267.     begin
  268.         oe := PBControl(ParmBlkPtr(cbp), true);
  269.         if oe <> noErr then
  270.             FreeCB(cbp);
  271.         PBControlAsync := oe;
  272.     end;
  273.  
  274.     function TCPGetMyIPAddr (var myIP: longInt): OSErr;
  275.         var
  276.             cb: TCPControlBlock;
  277.             oe: OSErr;
  278.     begin
  279.         ZeroCB(cb, nil, TCPcsGetMyIP);
  280.         oe := PBControlSync(cb);
  281.         myIP := cb.getmyip.ourAddress;
  282.         TCPGetMyIPAddr := oe;
  283.     end;
  284.  
  285.     procedure SetUserPtr (userptr: OSErrPtr; oe: OSErr);
  286.     begin
  287.         if userptr <> nil then begin
  288.             if oe <> noErr then
  289.                 userptr^ := oe;
  290.         end;
  291.     end;
  292.  
  293.     function CreateStream (var connection: TCPConnectionPtr): OSErr;
  294.         var
  295.             oe: OSErr;
  296.             cb: TCPControlBlock;
  297.     begin
  298.         connection := TCPConnectionPtr(NewPtr(sizeof(TCPConnectionType)));
  299.         if connection = nil then
  300.             oe := outOfMemory
  301.         else
  302.             with connection^ do begin
  303.                 magic := MAGICNUMBER;
  304.                 asends := 0;
  305.                 asendcompletes := 0;
  306.                 closedone := false;
  307.                 incomingSize := 0;
  308.                 ZeroCB(cb, nil, TCPcsCreate);
  309.                 cb.create.rcvBuff := @connection^.buffer;
  310.                 cb.create.rcvBuffLen := TCPBUFFERSIZE;
  311.                 oe := PBControlSync(cb);
  312.                 stream := cb.tcpStream;
  313.             end;
  314.         if (oe <> noErr) and (connection <> nil) then
  315.             DestroyConnection(connection);
  316.         CreateStream := oe;
  317.     end;
  318.  
  319.     function PAOpen (var connection: TCPConnectionPtr; cs: integer; localport: integer; remoteIP: longInt; remoteport: integer; userptr: OSErrPtr): OSErr;
  320.         var
  321.             oe, ooe: OSErr;
  322.             cbp: MyControlBlockPtr;
  323.             cb: TCPControlBlock;
  324.     begin
  325.         oe := CreateStream(connection);
  326.         if oe = noErr then begin
  327.             with connection^ do begin
  328.                 oe := GetCB(cbp, connection, cs, userptr, nil);
  329.                 if oe = noErr then begin
  330.                     cbp^.tcp.open.localPort := localPort;
  331.                     cbp^.tcp.open.remoteHost := remoteIP;
  332.                     cbp^.tcp.open.remotePort := remoteport;
  333.                     oe := PBControlAsync(cbp);
  334.                 end;
  335.                 if oe <> noErr then begin
  336.                     ZeroCB(cb, stream, TCPcsRelease);
  337.                     ooe := PBControlSync(cb);
  338.                     DestroyConnection(connection);
  339.                 end;
  340.             end;
  341.         end;
  342.         SetUserPtr(userptr, oe);
  343.         PAOpen := oe;
  344.     end;
  345.  
  346. { Open a connection to another machine }
  347.     function TCPActiveOpen (var connection: TCPConnectionPtr; localport: integer; remoteIP: longInt; remoteport: integer; userptr: OSErrPtr): OSErr;
  348.     begin
  349.         TCPActiveOpen := PAOpen(connection, TCPcsActiveOpen, localport, remoteIP, remoteport, userptr);
  350.     end;
  351.  
  352. { Open a socket on this machine, to wait for a connection }
  353.     function TCPPassiveOpen (var connection: TCPConnectionPtr; localport: integer; remoteIP: longInt; remoteport: integer; userptr: OSErrPtr): OSErr;
  354.     begin
  355.         TCPPassiveOpen := PAOpen(connection, TCPcsPassiveOpen, localport, remoteIP, remoteport, userptr);
  356.     end;
  357.  
  358.     function TCPRawReceiveChars (connection: TCPConnectionPtr; returnPtr: ptr; readCount: integer): OSErr;
  359. { Return readCount characters from the TCP connection. }
  360. { WARNING: Doesnt handle incoming buffer, so don't use with TCPReceiveUptp or ReadByte }
  361.         var
  362.             cb: TCPControlBlock;
  363.     begin
  364.         ZeroCB(cb, connection^.stream, TCPcsRcv);
  365.         cb.receive.rcvBuff := returnPtr;
  366.         cb.receive.rcvBuffLength := readCount;
  367.         TCPRawReceiveChars := PBControlSync(cb);
  368.     end;
  369.  
  370. { Return readCount characters from the TCP connection.}
  371.     function TCPReceiveChars (connection: TCPConnectionPtr; returnPtr: ptr; readCount: integer): OSErr;
  372.         var
  373.             readCountStr: Str255;
  374.             l: longInt;
  375.             p: Ptr;
  376.             oe: OSErr;
  377.             cb: TCPControlBlock;
  378.     begin
  379.         oe := ValidateConnection(connection);
  380.         if oe = noErr then
  381.             if readCount < 0 then
  382.                 oe := invalidLength
  383.             else if readCount > 0 then begin
  384.                 p := returnPtr;
  385.                 with connection^ do
  386.                     if incomingSize > 0 then begin
  387.             { Read as much as there is or as much as we need, whichever is less. }
  388.                         if readCount < incomingSize then
  389.                             l := readCount
  390.                         else
  391.                             l := incomingSize;
  392.                         BlockMove(incomingPtr, p, l);
  393.                         incomingPtr := Ptr(ord4(incomingPtr) + l);
  394.                         incomingSize := incomingSize - l;
  395.                         p := Ptr(ord4(p) + l);
  396.                         readCount := readCount - l;
  397.                     end;
  398.                 { If there's more needed, then read it from the connection. }
  399.                 if readCount > 0 then begin
  400.                         { Issue a read and wait until it all arrives). }
  401.                     oe := TCPRawReceiveChars(connection, p, readCount);
  402.                 end;
  403.             end;
  404.         TCPReceiveChars := oe;
  405.     end;
  406.  
  407.     function TCPReadByte (connection: TCPConnectionPtr; timeout: longInt; var b: SignedByte): OSErr;
  408.         { Return the next byte in the buffer, reading more in if necessary. }
  409.         var
  410.             waitUntil: longInt;
  411.             readIn: longInt;
  412.             oe: OSErr;
  413.             cb: TCPControlBlock;
  414.     begin
  415.         oe := ValidateConnection(connection);
  416.         if oe = noErr then
  417.             with connection^ do begin            { Check if we need to read in more bytes. }
  418.                 if incomingSize = 0 then begin
  419.                     if timeout = 0 then
  420.                         oe := commandTimeout
  421.                     else begin
  422.                         waitUntil := TickCount + timeout;
  423.     { keep on trying to read until we get at least one, or the time-out happens. }
  424.                         while (oe = noErr) and (incomingSize = 0) do begin                { Get the status. }
  425.                             readIn := TCPCharsAvailable(connection);    { If there's something there to read, do so. }
  426.                             if readIn > 0 then begin    { Don't read any more than will fit in the buffer. }
  427.                                 if readIn > INCOMINGBUFSIZE then
  428.                                     readIn := INCOMINGBUFSIZE;
  429.                         { Issue the read. }
  430.                                 oe := TCPRawReceiveChars(connection, @inBuf, readIn);
  431.                                 if oe = noErr then begin
  432.                                     incomingSize := readIn;
  433.                                     incomingPtr := @inBuf;
  434.                                 end;
  435.                             end        { If not, do another round or get out, depending on the timeout condition. }
  436.                             else if TickCount > waitUntil then begin
  437.                                 oe := commandTimeOut;
  438.                             end;
  439.                         end;
  440.                     end;
  441.                 end;
  442.                 { Get the byte to return. }
  443.                 if incomingSize > 0 then begin
  444.                     b := incomingPtr^;
  445.                     incomingPtr := Ptr(ord4(incomingPtr) + 1);
  446.                     incomingSize := incomingSize - 1;
  447.                 end
  448.                 else
  449.                     b := 0;
  450.             end;
  451.         TCPReadByte := oe;
  452.     end;
  453.  
  454. { Pass in a block of memory (readPtr,readSize), already containing readPos bytes}
  455. { TCPReceiveUpTo will then read characters until a termChar character is reached,}
  456. { or until waitForChars ticks go by without receiving any bytes.  If waitForChars is}
  457. { zero, then TCPReceiveUpTo will return immediately.  If termChar=0, then it}
  458. { will read the entire buffer, and any characters that arrive before a timeout }
  459.     function TCPReceiveUpTo (connection: TCPConnectionPtr; termChar: signedByte;{}
  460.                                     charTimeOut: longInt; readPtr: ptr; readSize: longInt; var readPos: longInt;{}
  461.                                     var gottermchar: boolean): OSErr;
  462.         var
  463.             oe: OSErr;
  464.  
  465.         procedure putByte (b: signedByte);
  466.         { Put the byte b after the output handle, increasing the handle's size in the process. }
  467.             var
  468.                 p: Ptr;
  469.         begin
  470.             p := Ptr(ord4(readPtr) + readPos);
  471.             p^ := b;
  472.             readPos := readPos + 1;
  473.         end;
  474.  
  475.         var
  476.             inChar: SignedByte;
  477.  
  478.     begin
  479.         oe := ValidateConnection(connection);
  480.         gottermchar := false;
  481. { Cycle until the timeout happens or we see the termintor character or we run out of room. }
  482.         while (oe = noErr) and (readPos < readSize) and not gottermchar do begin            { Get the next character. }
  483.             oe := TCPReadByte(connection, charTimeOut, inChar);                    { Ignore the character if it's a zero. }
  484.             if (oe = noErr) and (inChar <> 0) then begin            { Put it in the result. }
  485.                 putByte(inChar);                    { Check for the end. }
  486.                 gottermchar := inChar = termChar;
  487.             end;
  488.         end;
  489.         if oe = commandTimeOut then
  490.             oe := noErr;
  491.         TCPReceiveUpTo := oe;
  492.     end;
  493.  
  494.     function TCPSend (connection: TCPConnectionPtr; writePtr: ptr; writeCount: integer): OSErr;
  495.         var
  496.             wds: wdsType;
  497.             oe: OSErr;
  498.             cb: TCPControlBlock;
  499.             p: ptr;
  500.     begin
  501.         oe := ValidateConnection(connection);
  502.         if oe = nOErr then
  503.             if writeCount > 0 then begin
  504.                 wds.buffer := writePtr;
  505.                 wds.size := writeCount;
  506.                 wds.term := 0;
  507.                 ZeroCB(cb, connection^.stream, TCPcsSend);
  508.                 cb.send.wds := @wds;
  509.                 oe := PBControlSync(cb);
  510.             end
  511.             else if writeCount < 0 then
  512.                 oe := InvalidLength;
  513.         TCPSend := oe;
  514.     end;
  515.  
  516.     procedure TCPSendComplete (cbp: MyControlBlockPtr);
  517.         var
  518.             oe: OSErr;
  519.     begin
  520.         AddBlock(cbp^.tcp.send.wds);
  521.         with cbp^.tcpc^ do begin
  522.             asendcompletes := asendcompletes + 1;
  523.             if (asendcompletes = asends) and closedone then begin
  524.                 asendcompletes := asendcompletes - 1; { Avoid race condition with TCPClose }
  525.                 oe := GetCB(cbp, cbp^.tcpc, TCPcsClose, closeuserptr, nil);
  526. { GetCB won't NewPtr because the completion has just released a block }
  527.                 if oe = noErr then
  528.                     oe := PBControlAsync(cbp);
  529.             end;
  530.         end;
  531.     end;
  532.  
  533.     function TCPSendAsync (connection: TCPConnectionPtr; writePtr: ptr; writeCount: integer; userptr: OSErrPtr): OSErr;
  534.         type
  535.             myblock = record
  536.                     wds: wdsType;
  537.                     data: array[0..100] of byte;
  538.                 end;
  539.             myblockptr = ^myblock;
  540.         var
  541.             oe: OSErr;
  542.             cbp: MyControlBlockPtr;
  543.             p: myblockptr;
  544.     begin
  545.         oe := ValidateConnection(connection);
  546.         if oe = nOErr then
  547.             if writeCount > 0 then begin
  548.                 p := myblockptr(NewPtr(writeCount + SizeOf(wdsType)));
  549.                 if p = nil then
  550.                     oe := outOfMemory
  551.                 else begin
  552.                     p^.wds.buffer := @p^.data;
  553.                     p^.wds.size := writeCount;
  554.                     p^.wds.term := 0;
  555.                     with p^.wds do
  556.                         BlockMove(writePtr, buffer, size);
  557.                     oe := GetCB(cbp, connection, TCPcsSend, userptr, @TCPSendComplete);
  558.                     cbp^.tcp.send.wds := POINTER(p);
  559.                     with connection^ do
  560.                         asends := asends + 1;
  561.                     oe := PBControlAsync(cbp);
  562.                     if oe <> noErr then
  563.                         DisposPtr(ptr(p));
  564.                 end;
  565.             end
  566.             else if writeCount < 0 then
  567.                 oe := InvalidLength;
  568.         TCPSendAsync := oe;
  569.     end;
  570.  
  571.     function TCPClose (connection: TCPConnectionPtr; userptr: OSErrPtr): OSErr;
  572.         var
  573.             oe: OSErr;
  574.             cbp: MyControlBlockPtr;
  575.     begin
  576.         oe := ValidateConnection(connection);
  577.         if oe = noErr then
  578.             with connection^ do begin
  579.                 closeuserptr := userptr;
  580.                 if userptr <> nil then
  581.                     userptr^ := inProgress;
  582.                 closedone := true;
  583.                 if asends = asendcompletes then begin
  584.                     oe := GetCB(cbp, connection, TCPcsClose, userptr, nil);
  585.                     if oe = noErr then begin
  586.                         oe := PBControlAsync(cbp);
  587.                     end;
  588.                 end;
  589.             end;
  590.         SetUserPtr(userptr, oe);
  591.         TCPClose := oe;
  592.     end;
  593.  
  594.     function TCPAbort (connection: TCPConnectionPtr): OSErr;
  595.         var
  596.             oe: OSErr;
  597.             cb: TCPControlBlock;
  598.     begin
  599.         oe := ValidateConnection(connection);
  600.         if oe = noErr then begin
  601.             ZeroCB(cb, connection^.stream, TCPcsAbort);
  602.             oe := PBControlSync(cb);
  603.         end;
  604.         TCPAbort := oe;
  605.     end;
  606.  
  607. { Release the TCP stream, including the buffer.}
  608.     function TCPRelease (var connection: TCPConnectionPtr): OSErr;
  609.         var
  610.             oe: OSErr;
  611.             cb: TCPControlBlock;
  612.     begin
  613.         oe := ValidateConnection(connection);
  614.         if oe = noErr then begin
  615.             ZeroCB(cb, connection^.stream, TCPcsRelease);
  616.             oe := PBControlSync(cb);
  617.             DestroyConnection(connection);
  618.         end;
  619.         TCPRelease := oe;
  620.     end;
  621.  
  622. {    TCPRawState(connectionID) -- Return the state of the TCP connection.}
  623.     procedure TCPRawState (connection: TCPConnectionPtr; var state: integer; var localhost: longInt; var localport: integer; var remotehost: longInt; var remoteport: integer; var available: longInt);
  624.         var
  625.             cb: TCPControlBlock;
  626.             oe: OSErr;
  627.     begin
  628.         oe := ValidateConnection(connection);
  629.         localhost := 0;
  630.         localport := 0;
  631.         remotehost := 0;
  632.         remoteport := 0;
  633.         available := 0;
  634.         if oe <> noErr then begin
  635.             state := 99; { Error -> Closed }
  636.         end
  637.         else begin
  638.             ZeroCB(cb, connection^.stream, TCPcsStatus);
  639.             if PBControlSync(cb) <> noErr then begin
  640.                 state := 99; { Closed }
  641.             end
  642.             else begin
  643.                 state := cb.status.connectionState;
  644.                 localhost := cb.status.localhost;
  645.                 localport := cb.status.localport;
  646.                 remotehost := cb.status.remotehost;
  647.                 remoteport := cb.status.remoteport;
  648.                 available := cb.status.amtUnreadData + connection^.incomingSize;
  649.             end;
  650.         end;
  651.     end;
  652.  
  653. { Return the state of the TCP connection.}
  654.     function TCPState (connection: TCPConnectionPtr): TCPStateType;
  655.         var
  656.             state: integer;
  657.             localhost: longInt;
  658.             localport: integer;
  659.             remotehost: longInt;
  660.             remoteport: integer;
  661.             available: longInt;
  662.     begin
  663.         TCPRawState(connection, state, localhost, localport, remotehost, remoteport, available);
  664.         case state of
  665.             0: 
  666.                 TCPState := T_Closed;
  667.             2: 
  668.                 TCPState := T_Listening;
  669.             4, 6: 
  670.                 TCPState := T_Opening;
  671.             8: 
  672.                 TCPState := T_Established;
  673.             10, 12, 16, 18, 20: 
  674.                 TCPState := T_Closing;
  675.             14: 
  676.                 TCPState := T_PleaseClose;
  677.             98: 
  678.                 TCPState := T_WaitingForOpen;
  679.             99: 
  680.                 TCPState := T_Closed;
  681.             otherwise
  682.                 TCPState := T_Unknown;
  683.         end;
  684.     end;
  685.  
  686. {    Return the number of characters available for reading from the TCP connection.}
  687.     function TCPCharsAvailable (connection: TCPConnectionPtr): longInt;
  688.         var
  689.             state: integer;
  690.             localhost: longInt;
  691.             localport: integer;
  692.             remotehost: longInt;
  693.             remoteport: integer;
  694.             available: longInt;
  695.     begin
  696.         TCPRawState(connection, state, localhost, localport, remotehost, remoteport, available);
  697.         TCPCharsAvailable := available;
  698.     end;
  699.  
  700.     function TCPFlush (connection: TCPConnectionptr): OSErr;
  701.         var
  702.             buffer: array[0..255] of signedByte;
  703.             f: longInt;
  704.             oe: OSErr;
  705.     begin
  706.         f := TCPCharsAvailable(connection);
  707.         oe := noErr;
  708.         while (f > 0) and (oe = noErr) do begin
  709.             if f > 256 then
  710.                 f := 256;
  711.             oe := TCPReceiveChars(connection, @buffer, f);
  712.             if oe = noErr then
  713.                 f := TCPCharsAvailable(connection);
  714.         end;
  715.         TCPFlush := oe;
  716.     end;
  717.  
  718. end.